﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace MichaelTestApp
{
    public static class Utility
    {
        private static Vector CalculateRepulsion(KineticNode sourceNode, KineticNode targetNode)
        {
            Vector force = sourceNode.Location - targetNode.Location;

            return (sourceNode.Charge * targetNode.Charge * force) / (force.LengthSquared * 4 * Math.PI * m_permittivity);
        }


        private static Vector CalculateAttraction(KineticNode sourceNode, KineticNode targetNode, double edgeLength)
        {
            
            Vector force = sourceNode.Location - targetNode.Location;
            double displacement = force.Length - edgeLength;

            force.Normalize();

            force.Negate();

            return m_spring * force * displacement;
        }

        private static void CalculateEdgeRepulsion(KineticNode node, double width, double height)
        {
            Vector force = new Vector(0, 0);
            Vector displacement = new Vector(0, 0);

            List<Vector> sides = new List<Vector>()
            {
                new Vector(node.Location.X, 0),
                new Vector(node.Location.X, height),
                new Vector(width, node.Location.Y),
                new Vector(0, node.Location.Y)
            };
            //Vector top = new Vector(node.Location.X, 0);
            //Vector bottom = new Vector(node.Location.X, height);
            //Vector right = new Vector(width, node.Location.Y);
            //Vector left = new Vector(0, node.Location.Y);

            foreach (Vector side in sides)
            {
                displacement = node.Location - side;
                force = 1000 * ( displacement / displacement.Length );
                force /= Math.Exp(displacement.Length);
                node.NetForce += force;
            }
        }

        //private static void CheckBounds(List<KineticNode> graph, double width, double height)
        //{
        //    foreach (KineticNode node in graph)
        //    {
        //        case 
        //    }
        //}

        public static IEnumerable<KineticNode> CalculateNodePositions(
            List<KineticNode> graph, KineticNode root, double width, double height)
        {
            double totalKineticEnergy = 0;

            m_edgeLength = Math.Sqrt( Math.Pow(width, 2) + Math.Pow(height, 2)) / 6;
            KineticNode centerSpring = new KineticNode();

            centerSpring.Location = new Vector(width / 2, height / 2);

            double newCharge = Math.Pow(width * height, 0.5) / Math.Pow(graph.Count, 0.5);

            //m_permittivity = Math.Log10(width * height);

            foreach (KineticNode node in graph)
            {
                node.Velocity = new Vector(0, 0);
                node.Charge = newCharge;
                node.AddFrame(node.Location);
            }

            while (true)
            {
                totalKineticEnergy = 0;

                foreach (KineticNode node in graph)
                {
                    node.NetForce = new Vector(0, 0);

                    CalculateEdgeRepulsion(node, width, height);

                    //Vector top = new Vector(node.Location.X, 0);
                    //Vector bottom = new Vector(node.Location.X, height);
                    //Vector right = new Vector(0, node.Location.Y);
                    //Vector left = new Vector(width, node.Location.Y);

                    //Vector dude = new Vector(-1 * node.Location.X + width, -1 *  node.Location.Y);

                    //double one = Vector.AngleBetween(-1 * node.Location, right);
                    //double two = Vector.AngleBetween(dude, right);
                    //double temp;

                    //if (one > two)
                    //{
                    //    temp = one;
                    //    one = two;
                    //    two = temp;
                    //}
                    
                    //Vector hm = new Vector(Math.Cos(two) - Math.Cos(one), Math.Sin(two) - Math.Sin(one));

                    //hm *= 20;

                    //var sides = new List<KineticNode>()
                    //{
                    //    new KineticNode(top),
                    //    new KineticNode(bottom),
                    //    new KineticNode(right),
                    //    new KineticNode(left)
                    //};

                    //foreach (KineticNode side in sides)
                    //{
                    //    side.Charge = 5000 / (node.Location - side.Location).LengthSquared;
                    //}



                    //if (node == root)
                    //{
                    //    node.NetForce = CalculateAttraction(node, centerSpring, 0);
                    //}
                    //else
                    //{
                    //    var otherNodes = graph.Where(n => n != node);
                    //    foreach (KineticNode otherNode in otherNodes)
                    //    {
                    //        node.NetForce += CalculateRepulsion(node, otherNode);
                    //    }

                    //    foreach (KineticNode connectedNode in node.AdjacentNodes)
                    //    {
                    //        node.NetForce += CalculateAttraction(node, connectedNode, m_edgeLength);
                    //    }

                    //    //node.NetForce += hm;

                    //    //foreach (KineticNode side in sides)
                    //    //{
                    //    //    node.NetForce += CalculateRepulsion(node, side);
                    //    //}
                    //}

                    node.Velocity = (node.Velocity + m_timestep * node.NetForce) * m_damping;
                    node.Location += m_timestep * node.Velocity;
                    node.AddFrame(node.Location);
                    totalKineticEnergy += node.Mass * node.Velocity.LengthSquared;
                    
                } // End loop over graph nodes

                if (totalKineticEnergy < m_kineticEnergyCutoff)
                    break;


            }

            return graph;
        }

        private static double m_permittivity = .5;
        private static double m_timestep = 0.1;
        private static double m_damping = 0.5;
        private static double m_spring = 2;
        private static double m_kineticEnergyCutoff = 0.1;
        private static double m_edgeLength;

    }
}
